package fitness;

import optimizers.commons.GAConstants;
import optimizers.ganeat.Individual;
import model.Input;
import model.OligoSystemGeneral;

public class BistableFitness extends Fitness {

	public BistableResult evaluateFitness(OligoSystemGeneral oligoModel) {
		BistableResult result = new BistableResult(0, "", "");
		Input input1 = new Input(oligoModel.getId("i1"), 50, Integer.MAX_VALUE);
		Input input2 = new Input(oligoModel.getId("i2"), 50, Integer.MAX_VALUE);
		Input input3 = new Input(oligoModel.getId("i2"), 0, Integer.MAX_VALUE);
		Input[] inputs = new Input[] { input1, input2, input3 };

		double[][] timeSeries = oligoModel
				.getTimeSeriesWithInputComplexModel(inputs);

		if (input1.timeStep >= timeSeries[0].length) {
			return result;
		}

		result.firstScore = (double) 100 / (input1.timeStep);
		double[] stableStates = new double[] { timeSeries[0][input1.timeStep],
				timeSeries[1][input1.timeStep], timeSeries[2][input1.timeStep],
				timeSeries[3][input1.timeStep] };
		// double[][] timeSeriesWithInput1 =
		// calculatedTimeSeriesWithInput1(input);
		// Input input2 = getInput(indiv.NoOfSimpleSeq - 1,
		// timeSeriesWithInput1,
		// input.timeStep);
		if (input2.timeStep >= timeSeries[0].length) {
			return result;
		}
		double[] stableStates2 = new double[] { timeSeries[0][input2.timeStep],
				timeSeries[1][input2.timeStep], timeSeries[2][input2.timeStep],
				timeSeries[3][input2.timeStep] };
		double biggestDrop = 0;
		double biggestIncrease = 0;
		double maxConcentration = 0;
		int firstState = -1;
		for (int t = 0; t < timeSeries[0].length; t++) {
			for (int i = 0; i < timeSeries.length; i++) {
				if (timeSeries[i][t] > maxConcentration)
					maxConcentration = timeSeries[i][t];
			}
		}
		for (int i = 0; i < stableStates.length; i++) {
			double change = Math.abs(stableStates[i] - stableStates2[i]);
			if (stableStates[i] > stableStates2[i] && biggestDrop < change) {
				biggestDrop = change;
				firstState = i;
			}
			if (stableStates[i] < stableStates2[i] && biggestIncrease < change) {
				biggestIncrease = change;
			}
		}
		// System.out.println(biggestDrop);
		// System.out.println(biggestIncrease);
		if (biggestDrop > maxConcentration / 4
				&& biggestIncrease > maxConcentration / 4) {// state
			// changed
			result.changeCausedByInput1 += (double) 1000
					/ (input2.timeStep - input1.timeStep);

			// double[][] timeSeriesWithInput2 =
			// calculatedTimeSeriesWithInput2(new Input[] {
			// input, input2 });
			// Input input3 = getInput(2, timeSeriesWithInput2,
			// input2.timeStep);
			if (input3.timeStep >= timeSeries[0].length) {
				int tt = 0;
				int startedStable = 0;
				boolean stable = true;
				for (tt = input2.timeStep; tt < timeSeries[0].length; tt++) {
					if (Math.abs(timeSeries[firstState][tt]
							- timeSeries[firstState][tt - 1]) > GAConstants.changeThresholdStable) {
						stable = false;
					} else {
						stable = true;
					}
					if (stable) {
						if (startedStable == 0) {
							startedStable = tt;
						} else if (tt > startedStable
								+ GAConstants.timeToCheckStability) {
							break;
						}
					} else {
						startedStable = 0;
					}
				}
				result.changeCausedByInput2 += (double) (tt - input2.timeStep) / 500;
				return result;
			}
			double[] stableStates3 = new double[] {
					timeSeries[0][input3.timeStep],
					timeSeries[1][input3.timeStep],
					timeSeries[2][input3.timeStep],
					timeSeries[3][input3.timeStep] };
			biggestDrop = 0;
			biggestIncrease = 0;
			for (int i = 0; i < stableStates.length; i++) {
				double change = Math.abs(stableStates2[i] - stableStates3[i]);
				if (stableStates2[i] > stableStates3[i] && biggestDrop < change) {
					biggestDrop = change;
				}
			}
			double change = Math.abs(stableStates2[firstState]
					- stableStates3[firstState]);
			if (stableStates2[firstState] < stableStates3[firstState]
					&& biggestIncrease < change) {
				biggestIncrease = change;
			}
			// System.out.println(biggestDrop);
			// System.out.println(biggestIncrease);
			// System.out.println(maxConcentration);
			if (biggestDrop > maxConcentration / 4
					&& biggestIncrease > maxConcentration / 4) {// state
				// changed
				result.changeCausedByInput2 += result.firstScore
						+ result.changeCausedByInput1 + (double) 1000
						/ (input2.timeStep - input1.timeStep);
			} else {// give some score for the change
				int tt = 0;
				int startedStable = 0;
				for (tt = input2.timeStep; tt < timeSeries[0].length; tt++) {
					boolean stable = true;
					if (Math.abs(timeSeries[firstState][tt]
							- timeSeries[firstState][tt - 1]) > GAConstants.changeThresholdStable) {
						stable = false;
					}
					if (stable) {
						if (startedStable == 0) {
							startedStable = tt;
						} else if (tt > startedStable
								+ GAConstants.timeToCheckStability) {
							break;
						}
					} else {
						startedStable = 0;
					}
				}
				result.changeCausedByInput2 += (double) (tt - input2.timeStep) / 500;
			}
		} else {// give some score for the change
			result.changeCausedByInput1 += Math.sqrt(biggestIncrease) / 1000
					+ Math.sqrt(biggestDrop) / 1000;
		}

		return result;
	}

	@Override
	public BistableResult evaluateFitnessSimple(OligoSystemGeneral oligo) {
		BistableResult result = new BistableResult(0, "", "");
		
		Input input1 = new Input(oligo.nameToInt.get("i1"), 50,
				Integer.MAX_VALUE);
		Input input2 = new Input(oligo.nameToInt.get("i2"), 50,
				Integer.MAX_VALUE);
		Input input3 = new Input(oligo.nameToInt.get("i1"), 0,
				Integer.MAX_VALUE);
		Input[] inputs = new Input[] { input1, input2, input3 };
		// indiv.OligoModelComplex.setInput(inputs);
		double[][] timeSeries = oligo.getTimeSeriesWithInputSimpleModel(inputs);
		// Input input = getInput(indiv.NoOfSimpleSeq - 2, timeSeries, 50);
		if (input1.timeStep >= timeSeries[0].length) {
			return result;
		}

		result.firstScore = (double) 100 / (input1.timeStep);
		double[] stableStates = new double[] { timeSeries[0][input1.timeStep],
				timeSeries[1][input1.timeStep], timeSeries[2][input1.timeStep],
				timeSeries[3][input1.timeStep] };

		if (input2.timeStep >= timeSeries[0].length) {
			return result;
		}
		double[] stableStates2 = new double[] { timeSeries[0][input2.timeStep],
				timeSeries[1][input2.timeStep], timeSeries[2][input2.timeStep],
				timeSeries[3][input2.timeStep] };
		double biggestDrop = 0;
		double biggestIncrease = 0;
		double maxConcentration = 0;
		int firstState = -1;
		for (int t = 0; t < timeSeries[0].length; t++) {
			for (int i = 0; i < timeSeries.length; i++) {
				if (timeSeries[i][t] > maxConcentration)
					maxConcentration = timeSeries[i][t];
			}
		}
		for (int i = 0; i < stableStates.length; i++) {
			double change = Math.abs(stableStates[i] - stableStates2[i]);
			if (stableStates[i] > stableStates2[i] && biggestDrop < change) {
				biggestDrop = change;
				firstState = i;
			}
			if (stableStates[i] < stableStates2[i] && biggestIncrease < change) {
				biggestIncrease = change;
			}
		}
		// System.out.println(biggestDrop);
		// System.out.println(biggestIncrease);
		if (biggestDrop > maxConcentration / 4
				&& biggestIncrease > maxConcentration / 4) {// state
			// changed
			result.changeCausedByInput1 += (double) 1000
					/ (input2.timeStep - input1.timeStep);

			// double[][] timeSeriesWithInput2 =
			// calculatedTimeSeriesWithInput2(new Input[] {
			// input, input2 });
			// Input input3 = getInput(2, timeSeriesWithInput2,
			// input2.timeStep);
			if (input3.timeStep >= timeSeries[0].length) {
				int tt = 0;
				int startedStable = 0;
				boolean stable = true;
				for (tt = input2.timeStep; tt < timeSeries[0].length; tt++) {
					if (Math.abs(timeSeries[firstState][tt]
							- timeSeries[firstState][tt - 1]) > GAConstants.changeThresholdStable) {
						stable = false;
					} else {
						stable = true;
					}
					if (stable) {
						if (startedStable == 0) {
							startedStable = tt;
						} else if (tt > startedStable
								+ GAConstants.timeToCheckStability) {
							break;
						}
					} else {
						startedStable = 0;
					}
				}
				result.changeCausedByInput2 += (double) (tt - input2.timeStep) / 500;
				return result;
			}
			double[] stableStates3 = new double[] {
					timeSeries[0][input3.timeStep],
					timeSeries[1][input3.timeStep],
					timeSeries[2][input3.timeStep],
					timeSeries[3][input3.timeStep] };
			biggestDrop = 0;
			biggestIncrease = 0;
			for (int i = 0; i < stableStates.length; i++) {
				double change = Math.abs(stableStates2[i] - stableStates3[i]);
				if (stableStates2[i] > stableStates3[i] && biggestDrop < change) {
					biggestDrop = change;
				}
			}
			double change = Math.abs(stableStates2[firstState]
					- stableStates3[firstState]);
			if (stableStates2[firstState] < stableStates3[firstState]
					&& biggestIncrease < change) {
				biggestIncrease = change;
			}
			// System.out.println(biggestDrop);
			// System.out.println(biggestIncrease);
			// System.out.println(maxConcentration);
			if (biggestDrop > maxConcentration / 4
					&& biggestIncrease > maxConcentration / 4) {// state
				// changed
				result.changeCausedByInput2 += result.firstScore
						+ result.changeCausedByInput1 + (double) 1000
						/ (input2.timeStep - input1.timeStep);
			} else {// give some score for the change
				int tt = 0;
				int startedStable = 0;
				for (tt = input2.timeStep; tt < timeSeries[0].length; tt++) {
					boolean stable = true;
					if (Math.abs(timeSeries[firstState][tt]
							- timeSeries[firstState][tt - 1]) > GAConstants.changeThresholdStable) {
						stable = false;
					}
					if (stable) {
						if (startedStable == 0) {
							startedStable = tt;
						} else if (tt > startedStable
								+ GAConstants.timeToCheckStability) {
							break;
						}
					} else {
						startedStable = 0;
					}
				}
				result.changeCausedByInput2 += (double) (tt - input2.timeStep) / 500;
			}
		} else {// give some score for the change
			result.changeCausedByInput1 += Math.sqrt(biggestIncrease) / 1000
					+ Math.sqrt(biggestDrop) / 1000;
		}

		return result;
	}

	@Override
	public FitnessResult minFitness(OligoSystemGeneral indiv) {
		// TODO Auto-generated method stub
		return null;
	}

}
